//
// web_client.cpp
//
// Library: SocialBox
// Package:
// Module:  web_client
//
// Copyright (c) 2007, Bertrand Cachet and Contributors.
//

#include "web_client.hpp"

/****************************************************
*       POCO dependencies
****************************************************/
#include <Poco/HashMap.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPSClientSession.h>
#include <Poco/URI.h>
#include <Poco/StreamCopier.h>
#include <Poco/Net/NameValueCollection.h>

/****************************************************
*       BOOST dependencies
****************************************************/
#include <boost/algorithm/string/join.hpp>

/****************************************************
*       System dependencies
****************************************************/
#include <iostream>
#include <utility>
#include <string>
#include <list>

/****************************************************
*       SocialBox dependencies
****************************************************/

/****************************************************
*       Namespaces
****************************************************/
using namespace Poco;
using namespace std;

void cookiesToNameValueCollection(HashMap<string, string> *cookies, Net::NameValueCollection *nvc) {
    HashMap<string, string>::Iterator it;
    for (it=cookies->begin(); it != cookies->end(); it++)
	{
		cout << (*it).first << ":" << (*it).second << endl;
        nvc->add((*it).first, (*it).second);
	}
}

WebClient::WebClient(SynchronizationMode mode) {
    _init(mode);
}

WebClient::~WebClient() {
    delete _connections;
}

void WebClient::_init(SynchronizationMode mode) {
    _connections = new HashMap<string, WebClient::Request>();
    _synchronizationMode = mode;
    _host = "";
    _scheme = "";
    _authentication.needed = 0;
}

/*!
    * \brief Store requests to handle them when finished
    *
    * \param url URL of the associated request.
    * \param buffer Buffer where data will be stored.
*/
void WebClient::_registerRequest(string url, ostream *buffer) {
    Request req;
    req.buffer = buffer;
    req.status = Net::HTTPResponse::HTTP_OK;
    req.url = url;
    _connections->insert(pair<string, WebClient::Request>(url, req));
}

/*!
    * \brief Create request
    *
    * \param operation Get/Post method
    * \param _url URL to fetch
    * \param args Args associated to the POST method
    * \param to IOStream where to store data.
*/
int WebClient::_makeRequest(Operation operation, string _url, \
                            string args, ostream *to) {
    cout << "WebClient::_MakeRequest: " << endl;
    URI url(_url);
    int status = Net::HTTPResponse::HTTP_OK;

    string path(url.getPath());
    Net::HTTPClientSession session;
	Net::NameValueCollection _nvccookies;
    cout << "WebClient::_MakeRequest: Test Scheme" << endl;
    if (url.getScheme() == "https") {
         cout << "Create a HTTPS session with: " << url.getHost() << endl;
         Net::HTTPSClientSession session(url.getHost());
    } else {
        cout << "Create a HTTP session with: " << url.getHost() << endl;
        Net::HTTPClientSession session(url.getHost());
    }
    session.setHost(url.getHost());
    session.setPort(url.getPort());
    Net::HTTPRequest req;
    Net::HTTPResponse res;
    Net::NameValueCollection nvc;
    req.setURI(path);
    req.setVersion(Net::HTTPMessage::HTTP_1_1);
    switch (operation) {
        case GetOperation:
            req.setMethod(Net::HTTPRequest::HTTP_GET);
            break;
        case PostOperation:
            req.setMethod(Net::HTTPRequest::HTTP_POST);
            break;
        default:
            return -1;
            break;
    }
    if (!_cookies.empty()) {
        cookiesToNameValueCollection(&_cookies, &_nvccookies);
		cout << "Cookies size: " << _nvccookies.size() << endl;
        req.setCookies(_nvccookies);
    }
    cout << "Fetching URL: " << _url << endl;
    cout << "HOST: " << session.getHost() << endl;
    cout << "PORT: " << session.getPort() << endl;
    cout << "URI: " << req.getURI() << endl;
    cout << "Method: " << req.getMethod() << endl;
    cout << "Arguments: " << args << endl;
    
    // Send the request
    ostream& ost = session.sendRequest(req);

    ost << args;
    // Receive the response
    istream& rs = session.receiveResponse(res);
    status = res.getStatus();
    if (res.getContentLength() > 0)
        StreamCopier::copyStream(rs, *to);
    // Register the request
    _registerRequest(_url, to);
    /*
    if (_synchronizationMode == Synchronous){
        // We wait that request is finished to continue.
        
        //_eventLoop->exec();
        status = _connections->find(_url)->second.status;
        _connections->erase(_url);
    }
    */
    return status;
}

int WebClient::get(string url, ostream* to) {
    return _makeRequest(WebClient::GetOperation, url, string(), to);
}

int WebClient::post(string url, HashMap<string, string> args, ostream* to) {
    list<string> argList;
    HashMap<string, string>::Iterator it;
    for (it = args.begin(); it != args.end(); ++it)
        argList.push_back(it->first + "=" + it->second);
    return post(url, argList, to);
}


int WebClient::post(string url, list<string> args, ostream* to) {
    string aArgs = "";
    list<string>::iterator it;
    aArgs = boost::algorithm::join(args, "&");
    return _makeRequest(WebClient::PostOperation, url, aArgs, to);
}

int WebClient::getSynchronizationMode() {
    return _synchronizationMode;
}

void WebClient::addCookies(HashMap<string, string> cookies) {
    HashMap<string, string>::Iterator it;
    for (it=cookies.begin(); it!=cookies.end(); it++)
        _cookies.insert(pair<string, string>(it->first, it->second));
}

HashMap<string, string> WebClient::getCookies() {
    return _cookies;
}
